package samples

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runTest
import org.junit.Test

/**
 * 如果测试代码中有需要delay的逻辑，那么运行一个测试往往需要等待delay设置的时间，对于测试来说没必要等，因此可以使用runTest，
 * 它内部维护了一个虚拟时间（这个虚拟时间就是需要的测试时间数据），在runTest中，所有delay的逻辑都会被自动执行，不会阻塞测试线程，因此测试代码会快很多。
 * 你可能会有疑问，如果要测两种方式（比如suspend和concurrency）的代码执行速度，那么直接不用delay不行吗？
 * 答：如果不用delay，则两种方式的执行时间会差别很小，再加上每次执行时机器的反应速度不确定是否一样，所以会影响结果，加上delay会更准确
 */
@OptIn(ExperimentalCoroutinesApi::class) //runTest是实验API，加这个注解可以抑制相关warning
class SampleTest {
    @Test
    fun testDelayInSuspend() = runTest/*(Dispatchers.Default)*/ {
        val realStartTime = System.currentTimeMillis()
        val virtualStartTime = currentTime

        foo()

        println("${System.currentTimeMillis() - realStartTime} ms")  // ~ 6 ms
        println("${currentTime - virtualStartTime} ms")              // 1000 ms
    }

    suspend fun foo() {
        delay(1000) // auto-advances without delay
        println("foo")       // executes eagerly when foo() is called
    }

    @Test
    fun testDelayInLaunch() = runTest {
        val realStartTime = System.currentTimeMillis()
        val virtualStartTime = currentTime

        bar()

        println("${System.currentTimeMillis() - realStartTime} ms")  // ~ 11 ms
        println("${currentTime - virtualStartTime} ms")              // 1000 ms
    }

    suspend fun bar() = coroutineScope {
        //如果子协程更改了Dispatcher，没使用runTest继承来的Dispatcher，则测试不会奏效（不会抛出异常，但是结果不是有效的）
//        launch(Dispatchers.Default) {
        launch {
            delay(1000) // auto-advances without delay
            println("bar")       // executes eagerly when bar() is called
        }
    }
}